package oa.base.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.sun.mail.util.BASE64DecoderStream;
import com.sun.mail.util.BASE64EncoderStream;

/**
 * MD5 的加密和 SHA 加密 加密字符 或文件
 * 
 * @author lilang Create Date：2010-4-19 上午10:25:52
 */
public class MD5Ecnrypt {
	// 用来将字节转换成 16 进制表示的字符
	public static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	/**
	 * MAC算法可选以下多种算法
	 * 
	 * <pre>
	 * HmacMD5  
	 * HmacSHA1  
	 * HmacSHA256  
	 * HmacSHA384  
	 * HmacSHA512
	 * </pre>
	 */
	public static final String KEY_MAC = "HmacMD5";
	public static final String KEY_SHA = "SHA";
	public static final String KEY_MD5 = "MD5";

	/**
	 * 将字符串加密成MD5，32位16进制字串，如"3031209"转成"e043a49740adde7aae4f34818c52528e"
	 * 
	 * 
	 * 字符串转换为MD5
	 */
	public static String EncodeMD5Hex(String text) throws Exception {
		if(null==text){
			return null;
		}
		MessageDigest md = MessageDigest.getInstance(KEY_MD5);
		md.update(text.getBytes("US-ASCII"));
		byte[] digest = md.digest();
		StringBuffer md5 = new StringBuffer();
		for (int i = 0; i < digest.length; i++) {
			md5.append(Character.forDigit((digest[i] & 0xF0) >> 4, 16));
			md5.append(Character.forDigit((digest[i] & 0xF), 16));
		}
		return md5.toString();
	}

	/**
	 * MD5 对文件加密
	 * 
	 * @param path
	 * @return
	 */
	public String EncryptMD5File(String path) {
		File file = new File(path);
		try {
			if (!file.isFile()) {
				System.out.println("not a file");
				return null;
			}
			MessageDigest alga = MessageDigest.getInstance(KEY_MD5);
			FileInputStream fos = new FileInputStream(file);
			byte[] buffer = new byte[1024];// 设置填充大小
			int read = -1;// 实际读取的字节数
			while ((read = fos.read(buffer, 0, 1024)) > 0) {
				alga.update(buffer, 0, read);
			}
			byte[] b = alga.digest();
			fos.close();
			return byteToHexString(b);

		} catch (FileNotFoundException e) {
			System.out.println("系统找不到指定的文件" + path);
			System.out.println("file url not found");
			// e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * 把byte[]数组转换成十六进制字符串表示形式
	 * 
	 * @param tmp
	 *            要转换的byte[]
	 * @return 十六进制字符串表示形式
	 */
	private static String byteToHexString(byte[] tmp) {
		String s;
		// 用字节表示就是 16 个字节
		char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话，使用两个字符，
		// 所以表示成 16 进制需要 32 个字符
		int k = 0; // 表示转换结果中对应的字符位置
		for (int i = 0; i < 16; i++) { // 从第一个字节开始，对 MD5 的每一个字节
			// 转换成 16 进制字符的转换
			byte byte0 = tmp[i]; // 取第 i 个字节
			str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
			// >>> 为逻辑右移，将符号位一起右移
			str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
		}
		s = new String(str); // 换后的结果转换为字符串
		return s;
	}

	/** 将字符串加密成ASCII字串 */
	public static String EncodeMD5ASCII(String text) throws Exception {
		MessageDigest md = MessageDigest.getInstance(KEY_MD5);
		md.update(text.getBytes("US-ASCII"));
		byte[] digest = md.digest();
		return new String(digest, "US-ASCII");
	}

	/**
	 * 将ASCII字串编码成16进制字串
	 * 将ASCII字串编码成16进制字串,如：“?C??@??z?O4??RR?“转成”3f433f3f403f3f7
	 * a3f4f343f3f52523f“ 参数text 返回结果16进制字符串
	 */
	public static String DecodeMD5Hex(String text) throws Exception {
		byte[] digest = text.getBytes();
		StringBuffer md5 = new StringBuffer();
		for (int i = 0; i < digest.length; i++) {
			// Character.forDigit（int，int）确定使用指定基数的特定数字的字符表示形式。
			md5.append(Character.forDigit((digest[i] & 0xF0) >> 4, 16));
			md5.append(Character.forDigit((digest[i] & 0xF), 16));
		}
		return md5.toString();
	}

	/**
	 * 比较输入密码MD5加密后与数据库密码相等 参数a:输入的密码 参数b:数据库的ASCII字串
	 * 返回结果：a进行md5加密后和b相等则为真，反之则为假
	 */
	public static Boolean CheckPSW(String a, String b) throws Exception {
		String strone = EncodeMD5ASCII(a);
		return strone.equals(b);
	}

	/**
	 * SHA 加密
	 * 
	 * @param obj
	 * @return
	 */
	public byte[] EncryptSHA(byte[] obj) {
		MessageDigest sha = null;
		try {
			sha = MessageDigest.getInstance(KEY_SHA);
			sha.update(obj);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return sha.digest();

	}

	/**
	 * BASE64解密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return BASE64DecoderStream.decode(key.getBytes());
	}

	/**
	 * BASE64加密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptBASE64(byte[] key) throws Exception {
		return BASE64EncoderStream.encode(key);
	}

	/**
	 * 初始化HMAC密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String initMacKey() throws Exception {
		KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
		SecretKey secretKey = keyGenerator.generateKey();
		String keys = new String(encryptBASE64(secretKey.getEncoded()));
		return keys;
	}

	/**
	 * HMAC加密
	 * 
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
		SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		mac.init(secretKey);
		return mac.doFinal(data);

	}
	public static void main(String[] args) throws Exception {
		System.out.println(MD5Ecnrypt.EncodeMD5Hex("000000"));
	}

}